package org.pizza.dlock.distributed.lock.interceptor;

import org.pizza.dlock.distributed.lock.generator.LockKeyGenerator;
import org.pizza.dlock.distributed.lock.generator.DefaultLockKeyGenerator;
import org.pizza.dlock.distributed.lock.api.DLock;
import org.pizza.dlock.distributed.lock.api.DistributedLock;
import org.pizza.dlock.distributed.lock.exception.LockConflictException;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

/**
 * @author 高巍
 * @since 2019-11-27 10:42 上午
 * 注解redis分布式锁拦截器
 */

@Slf4j
public class AnnotationDistributedLockInterceptor implements MethodInterceptor, Ordered {

    private DistributedLock lock;
    private LockKeyGenerator generator;
    private final BeanFactory beanFactory;

    public AnnotationDistributedLockInterceptor(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        if (lock == null || generator == null) {
            this.determine();
        }
        //jdk动态代理的话这里就是个接口方法
        Method method = methodInvocation.getMethod();
        //这里需要获取指定类的方法
        Method mostSpecificMethod = ClassUtils.getMostSpecificMethod(method, methodInvocation.getThis().getClass());
        //获取方法注解
        DLock rLock = mostSpecificMethod.getAnnotation(DLock.class);
        boolean isThrow = rLock.isThrow();
        String key = this.generator.generateKey(methodInvocation);
        boolean isLocked = false;
        try {
            if (!(isLocked = lock.tryLock(key))) {
                if (isThrow) {
                    throw new LockConflictException(key);
                } else {
                    log.error("RLock --> lock key:[ " + key + " ] conflict");
                    return null;
                }
            }
            return methodInvocation.proceed();
        } finally {
            if (isLocked) {
                lock.unlock(key);
            }
        }
    }


    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }


    private void determine() {
        //double check
        if (this.lock == null) {
            synchronized (this) {
                if (this.lock == null) {
                    this.lock = this.beanFactory.getBean(DistributedLock.class);
                }
                if (generator == null) {
                    try {
                        this.generator = this.beanFactory.getBean(LockKeyGenerator.class);
                    } catch (NoSuchBeanDefinitionException e) {
                        this.generator = new DefaultLockKeyGenerator();
                    }
                }
            }
        }
    }

}
